use anyhow::Error;

use crate::{
    cmds::{
        connect::{auth::Auth, client::Client, echo::Echo, ping::Ping, select::Select}, hash::{
            hdel::Hdel, hexists::Hexists, hget::Hget, hgetall::Hgetall, hkeys::Hkeys, hlen::Hlen,
            hmget::Hmget, hmset::Hmset, hset::Hset, hsetnx::Hsetnx, hstrlen::Hstrlen, hvals::Hvals,
        }, key::{
            del::Del, exists::Exists, expire::Expire, expireat::ExpireAt, keys::Keys, persist::Persist, pexpire::Pexpire, pexpireat::PexpireAt, pttl::Pttl, randomkey::RandomKey, rename::Rename, renamenx::Renamenx, r#move::Move, ttl::Ttl, r#type::Type
        }, listing::{
            lindex::Lindex, llen::Llen, lpop::Lpop, lpush::Lpush, lpushx::Lpushx, lrange::Lrange,
            lset::Lset, ltrim::Ltrim, rpop::Rpop, rpush::Rpush, rpushx::Rpushx,
        }, server::{bgsave::Bgsave, dbsize::Dbsize, flushall::Flushall, flushdb::Flushdb, info::Info, save::Save}, server_sync::{psync::Psync, replconf::Replconf}, set::{
            sadd::Sadd, scard::Scard, sdiff::Sdiff, sinter::Sinter, sismember::Sismember, smembers::Smembers,
            spop::Spop, srem::Srem, sunion::Sunion, sunionstore::Sunionstore,
        }, sorted_set::{
            zadd::Zadd, zcard::Zcard, zcount::Zcount, zrank::Zrank, zrem::Zrem, zscore::Zscore,
        }, string::{
            append::Append, decr::Decr, decrby::Decrby, get::Get, getrange::GetRange, getset::GetSet, incr::Incr, incrby::Incrby, incrbyfloat::IncrbyFloat, mget::Mget, mset::Mset, set::Set, setrange::SetRange, strlen::Strlen
        }, transaction::{
            multi::Multi, exec::Exec, discard::Discard
        }, unknown::Unknown,
        json::{
            set::JsonSet,
            get::JsonGet,
            del::JsonDel,
        }
    },
    frame::Frame,
};

// 命令
pub enum Command {
    Auth(Auth),
    Append(Append),
    Client(Client),
    Dbsize(Dbsize),
    Expire(Expire),
    Del(Del),
    Keys(Keys),
    Flushdb(Flushdb),
    Get(Get),
    GetRange(GetRange),
    Ping(Ping),
    Pttl(Pttl),
    Select(Select),
    Set(Set),
    SetRange(SetRange),
    Ttl(Ttl),
    Unknown(Unknown),
    Mset(Mset),
    Mget(Mget),
    Strlen(Strlen),
    Sunionstore(Sunionstore),
    Renamenx(Renamenx),
    Rename(Rename),
    Exists(Exists),
    Hset(Hset),
    Hget(Hget),
    Type(Type),
    Hmset(Hmset),
    Hexists(Hexists),
    Hstrlen(Hstrlen),
    Hmget(Hmget),
    Hdel(Hdel),
    Hlen(Hlen),
    Hsetnx(Hsetnx),
    Hgetall(Hgetall),
    Hkeys(Hkeys),
    Lindex(Lindex),
    Persist(Persist),
    Rpop(Rpop),
    Lpop(Lpop),
    Llen(Llen),
    Hvals(Hvals),
    Rpush(Rpush),
    Lpush(Lpush),
    Sadd(Sadd),
    Sismember(Sismember),
    Smembers(Smembers),
    Scard(Scard),
    Sdiff(Sdiff),
    Sinter(Sinter),
    Spop(Spop),
    Srem(Srem),
    Flushall(Flushall),    Lpushx(Lpushx),
    Rpushx(Rpushx),
    Decr(Decr),
    Incr(Incr),
    IncrbyFloat(IncrbyFloat),
    Lset(Lset),
    Ltrim(Ltrim),
    Sunion(Sunion),
    Zcount(Zcount),
    Zadd(Zadd),
    Zscore(Zscore),
    Zcard(Zcard),
    Zrank(Zrank),
    Zrem(Zrem),
    Incrby(Incrby),
    Decrby(Decrby),
    Echo(Echo),
    ExpireAt(ExpireAt),
    RandomKey(RandomKey),
    PexpireAt(PexpireAt),
    Pexpire(Pexpire),
    Lrange(Lrange),
    Replconf(Replconf),
    Psync(Psync),
    Bgsave(Bgsave),
    Save(Save),
    GetSet(GetSet),
    Info(Info),
    Move(Move),
    // 事务命令
    Multi(Multi),
    Exec(Exec),
    Discard(Discard),
    // JSON命令
    JsonSet(JsonSet),
    JsonGet(JsonGet),
    JsonDel(JsonDel)
}

impl Command {
    pub fn parse_from_frame(frame: Frame) -> Result<Self, Error> {
        let command_name = frame.get_arg(0).unwrap();
        let command = match command_name.to_uppercase().as_str() {
            "AUTH" => Command::Auth(Auth::parse_from_frame(frame)?),
            "DEL" => Command::Del(Del::parse_from_frame(frame)?),
            "EXPIRE" => Command::Expire(Expire::parse_from_frame(frame)?),
            "FLUSHALL" => Command::Flushall(Flushall::parse_from_frame(frame)?),
            "FLUSHDB" => Command::Flushdb(Flushdb::parse_from_frame(frame)?),
            "GETRANGE" => Command::GetRange(GetRange::parse_from_frame(frame)?),
            "GET" => Command::Get(Get::parse_from_frame(frame)?),
            "PING" => Command::Ping(Ping::parse_from_frame(frame)?),
            "PTTL" => Command::Pttl(Pttl::parse_from_frame(frame)?),
            "TYPE" => Command::Type(Type::parse_from_frame(frame)?),
            "SELECT" => Command::Select(Select::parse_from_frame(frame)?),
            "SET" => Command::Set(Set::parse_from_frame(frame)?),
            "SETRANGE" => Command::SetRange(SetRange::parse_from_frame(frame)?),
            "TTL" => Command::Ttl(Ttl::parse_from_frame(frame)?),
            "RANDOMKEY" => Command::RandomKey(RandomKey::parse_from_frame(frame)?),
            "RENAME" => Command::Rename(Rename::parse_from_frame(frame)?),
            "EXISTS" => Command::Exists(Exists::parse_from_frame(frame)?),
            "STRLEN" => Command::Strlen(Strlen::parse_from_frame(frame)?),
            "MSET" => Command::Mset(Mset::parse_from_frame(frame)?),
            "MGET" => Command::Mget(Mget::parse_from_frame(frame)?),
            "APPEND" => Command::Append(Append::parse_from_frame(frame)?),
            "DBSIZE" => Command::Dbsize(Dbsize::parse_from_frame(frame)?),
            "HSET" => Command::Hset(Hset::parse_from_frame(frame)?),
            "HGET" => Command::Hget(Hget::parse_from_frame(frame)?),
            "HMSET" => Command::Hmset(Hmset::parse_from_frame(frame)?),
            "HDEL" => Command::Hdel(Hdel::parse_from_frame(frame)?),
            "HEXISTS" => Command::Hexists(Hexists::parse_from_frame(frame)?),
            "HSTRLEN" => Command::Hstrlen(Hstrlen::parse_from_frame(frame)?),
            "KEYS" => Command::Keys(Keys::parse_from_frame(frame)?),
            "HMGET" => Command::Hmget(Hmget::parse_from_frame(frame)?),
            "HLEN" => Command::Hlen(Hlen::parse_from_frame(frame)?),
            "HGETALL" => Command::Hgetall(Hgetall::parse_from_frame(frame)?),
            "HSETNX" => Command::Hsetnx(Hsetnx::parse_from_frame(frame)?),
            "HKEYS" => Command::Hkeys(Hkeys::parse_from_frame(frame)?),
            "PERSIST" => Command::Persist(Persist::parse_from_frame(frame)?),
            "LINDEX" => Command::Lindex(Lindex::parse_from_frame(frame)?),
            "RPOP" => Command::Rpop(Rpop::parse_from_frame(frame)?),
            "LPOP" => Command::Lpop(Lpop::parse_from_frame(frame)?),
            "LLEN" => Command::Llen(Llen::parse_from_frame(frame)?),
            "HVALS" => Command::Hvals(Hvals::parse_from_frame(frame)?),
            "RPUSH" => Command::Rpush(Rpush::parse_from_frame(frame)?),
            "LPUSH" => Command::Lpush(Lpush::parse_from_frame(frame)?),
            "SADD" => Command::Sadd(Sadd::parse_from_frame(frame)?),
            "SCARD" => Command::Scard(Scard::parse_from_frame(frame)?),
            "RENAMENX" => Command::Renamenx(Renamenx::parse_from_frame(frame)?),
            "EXPIREAT" => Command::ExpireAt(ExpireAt::parse_from_frame(frame)?),
            "SUNIONSTORE" => Command::Sunionstore(Sunionstore::parse_from_frame(frame)?),
            "SISMEMBER" => Command::Sismember(Sismember::parse_from_frame(frame)?),
            "SMEMBERS" => Command::Smembers(Smembers::parse_from_frame(frame)?),
            "SPOP" => Command::Spop(Spop::parse_from_frame(frame)?),
            "SREM" => Command::Srem(Srem::parse_from_frame(frame)?),
            "LPUSHX" => Command::Lpushx(Lpushx::parse_from_frame(frame)?),
            "RPUSHX" => Command::Rpushx(Rpushx::parse_from_frame(frame)?),
            "INCR" => Command::Incr(Incr::parse_from_frame(frame)?),
            "DECR" => Command::Decr(Decr::parse_from_frame(frame)?),
            "LSET" => Command::Lset(Lset::parse_from_frame(frame)?),
            "LTRIM" => Command::Ltrim(Ltrim::parse_from_frame(frame)?),
            "SUNION" => Command::Sunion(Sunion::parse_from_frame(frame)?),
            "ZCOUNT" => Command::Zcount(Zcount::parse_from_frame(frame)?),
            "ZADD" => Command::Zadd(Zadd::parse_from_frame(frame)?),
            "ZCARD" => Command::Zcard(Zcard::parse_from_frame(frame)?),
            "ZSCORE" => Command::Zscore(Zscore::parse_from_frame(frame)?),
            "ZREM" => Command::Zrem(Zrem::parse_from_frame(frame)?),
            "SDIFF" => Command::Sdiff(Sdiff::parse_from_frame(frame)?),
            "SINTER" => Command::Sinter(Sinter::parse_from_frame(frame)?),
            "ZRANK" => Command::Zrank(Zrank::parse_from_frame(frame)?),
            "INCRBY" => Command::Incrby(Incrby::parse_from_frame(frame)?),
            "INCRBYFLOAT" => Command::IncrbyFloat(IncrbyFloat::parse_from_frame(frame)?),
            "DECRBY" => Command::Decrby(Decrby::parse_from_frame(frame)?),
            "ECHO" => Command::Echo(Echo::parse_from_frame(frame)?),
            "PEXPIRE" => Command::Pexpire(Pexpire::parse_from_frame(frame)?),
            "PEXPIREAT" => Command::PexpireAt(PexpireAt::parse_from_frame(frame)?),
            "REPLCONF" => Command::Replconf(Replconf::parse_from_frame(frame)?),
            "LRANGE" => Command::Lrange(Lrange::parse_from_frame(frame)?),
            "PSYNC" => Command::Psync(Psync::parse_from_frame(frame)?),
            "GETSET" => Command::GetSet(GetSet::parse_from_frame(frame)?),
            "CLIENT" => Command::Client(Client::parse_from_frame(frame)?),
            "INFO" => Command::Info(Info::parse_from_frame(frame)?),
            "MOVE" => Command::Move(Move::parse_from_frame(frame)?),
            "MULTI" => Command::Multi(Multi::parse_from_frame(frame)?),
            "EXEC" => Command::Exec(Exec::parse_from_frame(frame)?),
            "DISCARD" => Command::Discard(Discard::parse_from_frame(frame)?),
            // JSON命令解析
            "JSON.SET" => Command::JsonSet(JsonSet::parse_from_frame(frame)?),
            "JSON.GET" => Command::JsonGet(JsonGet::parse_from_frame(frame)?),
            "JSON.DEL" => Command::JsonDel(JsonDel::parse_from_frame(frame)?),
            _ => Command::Unknown(Unknown::parse_from_frame(frame)?),
        };
        Ok(command)
    }

    pub fn propagate_aof_if_needed(&self) -> bool {
        match self {
            Command::Del(_) |
            Command::Expire(_) |
            Command::ExpireAt(_) |
            Command::Persist(_) |
            Command::Pexpire(_) |
            Command::PexpireAt(_) |
            Command::Rename(_) |
            Command::Renamenx(_) |
            Command::Append(_) |
            Command::Decr(_) |
            Command::Decrby(_) |
            Command::GetSet(_) |
            Command::Incr(_) |
            Command::Incrby(_) |
            Command::IncrbyFloat(_) |
            Command::Mset(_) |
            Command::Set(_) | 
            Command::SetRange(_) |
            Command::Flushall(_) |
            Command::Flushdb(_) |
            Command::Hdel(_) |
            Command::Hmset(_) |
            Command::Hset(_) |
            Command::Hsetnx(_) |
            Command::Lpop(_) |
            Command::Lpush(_) |
            Command::Lpushx(_) |
            Command::Lset(_) |
            Command::Ltrim(_) |
            Command::Rpop(_) |
            Command::Rpush(_) |
            Command::Rpushx(_) |
            Command::Sadd(_) |
            Command::Sdiff(_) |
            Command::Sinter(_) |
            Command::Spop(_) |
            Command::Srem(_) |
            Command::Sunionstore(_) |
            Command::Zadd(_) |
            Command::Zrem(_) |
            Command::Move(_) |
            Command::JsonSet(_) |
            Command::JsonDel(_) => true,
            _ => false,
        }
    }
}